#include "FileSystem.h"

FileSystem::FileSystem(){
    _localDirectory = "VFS_files";
    // verify if local directory exists 
    struct stat st;
    if(stat(_localDirectory.c_str(),&st) == 0){
        DBG_PRINTF(("directory is present\n"));
    } else {   
        mkdir(_localDirectory.c_str(), 0750);
    }
    _pointer = 0;
    pthread_mutex_init (&files_mutex, NULL);
    pthread_mutex_init (&file_writing_mutex, NULL);
}

FileSystem::~FileSystem(){
    while(!_files.empty()){
        File * temp = _files.back();
        _files.pop_back();
        if (temp != NULL){
            delete temp;
        }
    }
    pthread_mutex_destroy (&files_mutex);
    pthread_mutex_destroy (&file_writing_mutex);
}

string FileSystem::convertInt(int number)
{
   stringstream ss;
   ss << number;
   return ss.str();
}


// should not use mutex in private functions, assume mutex is already acquired in public functions
int FileSystem::findFileIndex(string fileName, int &index){
    int status = errNoFileFound;
    for (unsigned int i = 0; i < _files.size(); i++){
        if(_files[i]->fileName() == fileName){
            index = i; 
            status = SAFE;
            break;
        }
    }
    return status;
}

// VFS operations
void FileSystem::list(){
    pthread_mutex_lock (&files_mutex);    
    cout << setfill(' ');
    cout << "|" << setw(tableWidth) << "File Name"; 
    cout << "|" << setw(tableWidth) << "Version Number";
    cout << "|" << setw(tableWidth) << "Availability";
    cout << "|" << setw(tableWidth) << "Modified";
    cout << "|" << endl;

    cout << setfill('-');

    cout << "|" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "+" << setw(tableWidth) << "-";
    cout << "|" << endl;
    cout << setfill(' ');

    for (unsigned int i = 0; i < _files.size(); i++) {
        File *curFile = _files[i];
        File::Status status = curFile->status(0);
        if(status != File::DELETED){
            for(int j = 0; j <= curFile->versionNum(); j++) {
                    cout << "|" << setw(tableWidth) << curFile->fileName();
                    cout << "|" << setw(tableWidth) << j;

                    // availability logic
                    string availability = "Not Available";
                    if(curFile->isAvailable(j)){
                        availability = "Available";
                    }

                    // modified logic
                    string modified = "N";
                    if(curFile->isModified(j)){
                        modified = "Y";
                    }

                    cout << "|" << setw(tableWidth) << availability;
                    cout << "|" << setw(tableWidth) << modified;
                    cout << "|" << endl;

                    cout << setfill('-');

                    cout << "|" << setw(tableWidth) << "-";
                    cout << "+" << setw(tableWidth) << "-";
                    cout << "+" << setw(tableWidth) << "-";
                    cout << "+" << setw(tableWidth) << "-";
                    cout << "|" << endl;

                    cout << setfill(' ');
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
}

int FileSystem::modify(string fileName, int versionNum){
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->setModified(true, versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return status;
}

int FileSystem::insert(string fileName, int timeStamp){
    int status = SAFE;
    string shortName = getShortName(fileName);
    pthread_mutex_lock (&file_writing_mutex);
    ifstream ifile(fileName.c_str());
    if (ifile) {
        // copy file to local directory
        string output = _localDirectory+"/0_"+shortName;
        ofstream ofile (output.c_str(), fstream::trunc|fstream::binary);
        ofile << ifile.rdbuf();  
        ofile.close(); 
    }
    ifile.close();
    pthread_mutex_unlock (&file_writing_mutex);
    pthread_mutex_lock (&files_mutex);
    DBG_PRINTF(("Calling AddFile...\n"));
    int index = 0;
    status = findFileIndex(shortName, index);
    if(status != SAFE){
        addFile(shortName, 0);
        status = findFileIndex(shortName, index);
    }
    status = _files[index]->receivedLocalCopy(0);
    DBG_PRINTF(("Updating file %s with timestamp %i\n", fileName.c_str(), timeStamp));
    DBG_PRINTF(("Updating file %s with timestamp %i\n", fileName.c_str(), timeStamp));
    status = _files[index]->updateTimestamp(0, NULL, timeStamp);
    status = _files[index]->updateVersion(0, timeStamp);
    DBG_PRINTF(("now file %s has timestamp %i\n", fileName.c_str(), _files[index]->networkTimestamp(0)));
    DBG_PRINTF(("File Vector now has Size %i\n", (int)_files.size()));
    pthread_mutex_unlock (&files_mutex);
    DBG_PRINTF(("VFS->insert returning with status %i\n", status));
    return status;
}

int FileSystem::save(string fileName, int versionNum, int timeStamp){
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->setModified(false, versionNum);
        status = _files[index]->updateTimestamp(versionNum, NULL, timeStamp);   // set host and networkTimestamp
        status = _files[index]->updateVersion(versionNum, timeStamp);           // set currentTimestamp
        status = _files[index]->receivedLocalCopy(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return status;
}

int FileSystem::tag(string fileName, int versionNum, int timeStamp){
    int status = SAFE;
    int newTagVersion = 0;
    string longName = "";
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        newTagVersion = _files[index]->versionNum() + 1;       
    }
    pthread_mutex_unlock (&files_mutex);
    longName = _localDirectory+"/"+convertInt(versionNum)+"_"+fileName;
    pthread_mutex_lock (&file_writing_mutex);
    ifstream ifile(longName.c_str());
    if (ifile) {
        // copy file to local directory
        string output = _localDirectory+"/"+convertInt(newTagVersion)+"_"+fileName;
        ofstream ofile (output.c_str(), fstream::trunc|fstream::binary);
        ofile << ifile.rdbuf();   
        ofile.close();
    }
    ifile.close();
    pthread_mutex_unlock (&file_writing_mutex);
    pthread_mutex_lock (&files_mutex);
    addVersion(fileName, newTagVersion, NULL, timeStamp);
    _files[index]->receivedLocalCopy(newTagVersion);
    _files[index]->updateVersion(newTagVersion, timeStamp);
    DBG_PRINTF(("File Vector now has Size %i\n", (int)_files.size()));
    pthread_mutex_unlock (&files_mutex);
    return newTagVersion;
}

// stuff to do for peer
int FileSystem::getHostedFiles(string &message){
    int count = 0;
    // assumes entire information is shorter than max message length (bufSize)
    message = "";
    // loop through each version to check
    // record if networktimestamp == currenttimestamp
    // and host == NULL
    pthread_mutex_lock (&files_mutex);
    for(unsigned int i =0; i < _files.size(); i++){
        File * curr = _files[i];
        if(curr->status(0) != File::DELETED){
            for(int j = 0; j <= curr->versionNum(); j++){
                int upToDate = curr->isVersionUpToDate(j);
                if(upToDate == SAFE && curr->host(j) == NULL){
                    message = message + "%" + curr->fileName() + 
                                        "%" + convertInt(j);
                    count++;
                }
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
    message = convertInt(count) + message ;

    return SAFE;
}

bool FileSystem::isUpToDate(string fileName, int versionNum){
    int status = SAFE;
    bool result = false;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->isVersionUpToDate(versionNum);
        if(status == SAFE){
            result = true;
        }
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

bool FileSystem::hasLocalCopy(string fileName, int versionNum){
    int status = SAFE;
    bool result = false;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        result = _files[index]->hasLocalCopy(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

File::Status FileSystem::status(string fileName, int versionNum){
    int status = SAFE;
    File::Status result = File::NOCOPY;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        result = _files[index]->status(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

int FileSystem::getPhysicalFile(string fileName, int versionNum, vector<string> &chunks, int &timeStamp ){
    int status = SAFE;
    string physicalName = _localDirectory+"/"+convertInt(versionNum)+"_"+fileName;

    // get the currentTimestamp
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        timeStamp = _files[index]->networkTimestamp(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);

    pthread_mutex_lock(&file_writing_mutex);
    // generate vector of chunks
    if(status != errNoFileFound){
        ifstream ifile(physicalName.c_str());
        if(ifile){
            // get length of file:
            ifile.seekg (0, ios::end);
            long length = ifile.tellg();
            ifile.seekg (0, ios::beg);
                
            int chunkNum = length/chunkSize+2;

            chunks.clear();
            for(int i = 1; i < chunkNum; i++){
                // read data as a block:
                DBG_PRINTF(("reading chunk %i length: %li chunkSize: %i \n", i, length, chunkSize));
                char data[chunkSize];
                if(length < chunkSize){
                    ifile.read(data, length);
                    string chunk(data, length);
                    chunks.push_back(chunk);
                DBG_PRINTF(("strlen(data) returns %i \n", (int)strlen(data)));
                DBG_PRINTF(("reading chunk %i chunk length: %i \n", i, (int)chunk.length()));
                }else{
                    ifile.read(data, chunkSize);
                    string chunk(data, chunkSize);
                DBG_PRINTF(("strlen(data) returns %i \n", (int)strlen(data)));
                DBG_PRINTF(("reading chunk %i chunk length: %i \n", i, (int)chunk.length()));
                    chunks.push_back(chunk);
                }           
                length -= chunkSize;
            }     
        }else{
            DBG_PRINTF(("cannot open physical file %s even though logical file exists\n", physicalName.c_str()));
            status = errNoFileFound;
        }
        ifile.close();
    }
    pthread_mutex_unlock(&file_writing_mutex); 

    return status;
}

int FileSystem::getFileSystem(string ip, int port, string &message){
    // assumes entire information is shorter than max message length (bufSize)
    // message contains: # of files (in logical system, not physical)
    // then, per file: filename/versionNum/
    //  then, per version: version/networktimestamp/hostaddress(0 if null)/hostport(0 if null)/
    int count = 0;
    message = "";
    // record each version's info
    pthread_mutex_lock (&files_mutex);
    for(unsigned int i =0; i < _files.size(); i++){
        File * curr = _files[i];
        // transfer only if file is not deleted
        DBG_PRINTF(("current file is %i\n", i));
        if(curr->status(0) != File::DELETED){
            DBG_PRINTF(("access to status success\n"));
            message = message + "%" + curr->fileName() + 
                                "%" + convertInt(curr->versionNum());
            count++;
            DBG_PRINTF(("access to file name success\n"));
            for(int j = 0; j <= curr->versionNum(); j++){
                DBG_PRINTF(("%i, version number is %i\n", j, curr->versionNum()));
                Host * host = curr->host(j);
                int networkTimestamp = curr->networkTimestamp(j);
                message = message + "%" + convertInt(j) + 
                                    "%" + convertInt(networkTimestamp);
                DBG_PRINTF(("current version is up to date? %i\n", curr->isVersionUpToDate(j)));
                if(host != NULL){
                    message = message + "%" + host->ip() + 
                                        "%" + convertInt(host->port());
                }else if(curr->isVersionUpToDate(j) == SAFE){
                    message = message + "%" + ip + 
                                        "%" + convertInt(port);
                }else{
                    message = message + "%0%0";
                }
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
    message = convertInt(count) + message + "%";
    return SAFE;
}

vector<FileInfo*> FileSystem::SyncFileSystem(char * message){
    pthread_mutex_lock (&files_mutex);

    vector<FileInfo*> deletes;

    DBG_PRINTF(("Syncing FileSystem\n"));

    // parse buf
    char *field, *brkt;
    int fileCount = 0;

    DBG_PRINTF(("received msg : %s with length %i\n", message, (int)strlen(message)+1));

    field = strtok_r(message, "^%$", &brkt);
    fileCount = atoi(field);
    DBG_PRINTF(("number of files on network: %i\n", fileCount));


    // loop to get info on every file
    for(int i = 0; i < fileCount; i++){
        string fileName = "";
        int versionNum = 0;
        int state = SAFE;
        int index = 0;
        
        field = strtok_r(NULL, "^%$", &brkt);
        fileName = field;

        field = strtok_r(NULL, "^%$", &brkt);
        versionNum = atoi(field);
        state = findFileIndex(fileName, index);

        if(state == errNoFileFound){
            // file does not exist on local. create a new file and push!
            File * newFile = new File(fileName, 0);
            _files.push_back(newFile);
            state = findFileIndex(fileName, index);  // this should not return error again
        }

        // file exists on local. must compare networkTimestamps and such
        int myVersionNum = _files[index]->versionNum();
        
        // loop through all the versions!
        for(int i = 0; i <= versionNum; i++){
            int currVersion = 0;
            field = strtok_r(NULL, "^%$", &brkt);
            currVersion = atoi(field);

            int hisTimeStamp = 0;
            field = strtok_r(NULL, "^%$", &brkt);
            hisTimeStamp = atoi(field);

            string hisHostIP = "";
            field = strtok_r(NULL, "^%$", &brkt);
            hisHostIP = field;

            int hisHostPort = 0;
            field = strtok_r(NULL, "^%$", &brkt);
            hisHostPort = atoi(field);

            Host * hisHost = NULL;
            if(hisHostPort != 0 && hisHostIP != "0"){
                hisHost = new Host(hisHostIP, hisHostPort);
                DBG_PRINTF(("NEW FILE WITH HOST IP %s PORT %i", hisHostIP.c_str(), hisHostPort));
            }

            if(myVersionNum < currVersion){
                // I don't have records for this version, add new version
                _files[index]->addVersion(currVersion, hisHost, hisTimeStamp);
            }else{
                // I do have this version, update if hisTimeStamp > mynetworkTimeStamp.
                int mytimestamp = _files[index]->networkTimestamp(currVersion);
                if(mytimestamp <= hisTimeStamp){
                    _files[index]->updateTimestamp(currVersion, hisHost, hisTimeStamp);
                }else{
                    // if this is a deleted file, pop it into the vector
                    if(currVersion == 0 && _files[index]->status(currVersion) == File::DELETED ){
                        FileInfo * deleted = new FileInfo(_files[index]->fileName(), 0, _files[index]->networkTimestamp(0));
                        deletes.push_back(deleted);
                    }      
                }
            } 
        }
    }

    pthread_mutex_unlock (&files_mutex);
    return deletes;
}

int FileSystem::removeHost(string ip, int port){    
    // loop through each version to check
    // if host matches ip and port, let's change it to NULL
    DBG_PRINTF(("in removeHost with ip: %s, port: %i", ip.c_str(), port));
    pthread_mutex_lock (&files_mutex);
    for(unsigned int i =0; i < _files.size(); i++){
        //DBG_PRINTF(("current i: %i", i));
        File * curr = _files[i];
        for(int j = 0; j <= curr->versionNum(); j++){
            //DBG_PRINTF(("current j: %j", i));
            Host * temp = curr->host(j);
            if(temp != NULL && temp->ip() == ip && temp->port() == port){
                delete temp;
                curr->setHost(j,NULL);
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
    return SAFE;
}

vector<FileInfo*> FileSystem::filesToBroadcast(){
    vector<FileInfo*> temp;
    pthread_mutex_lock (&files_mutex);
    for(unsigned int i =0; i < _files.size(); i++){
        File * curr = _files[i];
        for(int j = 0; j <= curr->versionNum(); j++){
            int upToDate = curr->isVersionUpToDate(j);
            if(upToDate == SAFE && curr->host(j) == NULL){
                FileInfo * hostFile = new FileInfo(curr->fileName(), j, curr->networkTimestamp(j));
                temp.push_back(hostFile);
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
    return temp;
}

bool FileSystem::isModified(string fileName, int versionNum){
    int status = SAFE;
    bool result = false;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        result = _files[index]->isModified(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

bool FileSystem::exist(string fileName, int versionNum){
    int status = SAFE;
    bool result = false;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        File::Status status = _files[index]->status(0);
        if(status != File::DELETED){
            result = versionNum <=_files[index]->versionNum();
        }
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

Host * FileSystem::getHost(string fileName, int versionNum){
    int status = SAFE;
    Host * result = NULL;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        result = _files[index]->host(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

string FileSystem::getShortName(string longName){
    // copy file to local directory
    string shortName = longName;
    // if the file has an path, then find the actual file name
    size_t backSlash = longName.rfind("/");
    if (backSlash != string::npos)
    {
        shortName = longName.substr(backSlash+1, longName.length() - backSlash);
    }
    return shortName;
}


void FileSystem::setVersionInTransit(string fileName, int versionNum){
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->setVersionInTransit(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return;
}

bool FileSystem::isVersionInTransit(string fileName, int versionNum){
    int status = SAFE;
    bool result = false;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        result = _files[index]->isVersionInTransit(versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    return result;
}

void FileSystem::removeAllHosts(){
    pthread_mutex_lock (&files_mutex);
    for (unsigned int i = 0; i < _files.size(); i++){
        int versions = _files[i]->versionNum();
        for(int j = 0; j<=versions; j++){
            _files[i]->setHost(j, NULL);
        }
    }
    pthread_mutex_unlock (&files_mutex);
}

Host * FileSystem::getNextFileToPull(string &fileName, int &versionNum){
    Host * host = NULL;
    fileName = "";
    versionNum = -1;
    pthread_mutex_lock (&files_mutex);
    unsigned int size = _files.size();
    for (unsigned int i = 0; i < size; i++){
        int versions = _files[(i+_pointer)%size]->versionNum();
        for(int j = 0; j<=versions; j++){
            if(_files[(i+_pointer)%size]->host(j) != NULL 
                && _files[(i+_pointer)%size]->isVersionUpToDate(j) == errNotUpToDate){
                host = _files[(i+_pointer)%size]->host(j);
                fileName = _files[(i+_pointer)%size]->fileName();
                versionNum = j;
                _pointer = (_pointer + i) % size;
                pthread_mutex_unlock (&files_mutex);
                return host;
            }
        }
    }
    pthread_mutex_unlock (&files_mutex);
    return host;
}


// disk operations
int FileSystem::writeToDisk(string fileName, int versionNum, int chunkNum, char * content, int responseLen){
    pthread_mutex_lock(&file_writing_mutex);
    string output = _localDirectory+"/"+convertInt(versionNum)+"_"+fileName+".part"+convertInt(chunkNum);
    ofstream ofile (output.c_str(), fstream::trunc|ofstream::binary);
    ofile.write(content, responseLen);
    ofile.close();
    pthread_mutex_unlock(&file_writing_mutex);
    return SAFE;
}

int FileSystem::combineFileOnDisk(string fileName, int versionNum, int totalChunks){
    int status = SAFE;
    pthread_mutex_lock(&file_writing_mutex);
    DBG_PRINTF(("Combining all the file parts...\n"));
    
    DBG_PRINTF(("File %s version %i with %i chunks\n", fileName.c_str(), versionNum, totalChunks));

    string finaloutput = _localDirectory+"/"+convertInt(versionNum)+"_"+fileName;
    // remove the file if exist
    ifstream file(finaloutput.c_str());
    if(file){
        if( remove( finaloutput.c_str() ) != 0 ){
            DBG_PRINTF(( "Error deleting file: %s\n", finaloutput.c_str() ));
        } else {
            DBG_PRINTF(( "File successfully deleted %s\n", finaloutput.c_str()  ));
        }
    }
    ofstream ofile (finaloutput.c_str(), fstream::trunc|ofstream::binary);

    for(int i = 1; i <= totalChunks; i++){
        string input = _localDirectory+"/"+convertInt(versionNum)+"_"+fileName+".part"+convertInt(i);
        ifstream ifile(input.c_str());
        if(ifile){
            ofile<<ifile.rdbuf();
            // remove the partial file
            if( remove( input.c_str() ) != 0 ){
                DBG_PRINTF(( "Error deleting file: %s\n", input.c_str() ));
            } else {
                DBG_PRINTF(( "File successfully deleted %s\n", input.c_str()  ));
            }
        }else{
            status = errChunkNotFound;
            break;
        }
        ifile.close();
    }
    ofile.close();
    pthread_mutex_unlock(&file_writing_mutex);

    return status;
}


// some file operations in the background 
int FileSystem::addFile(string fileName, int versionNum){
    int status = SAFE;
    File * newFile = new File(fileName, versionNum);
    _files.push_back(newFile);
    return status;
}

// newly tagged version available on network
int FileSystem::addVersion(string fileName, int versionNum, Host * host, int timestamp){
    int status = SAFE;
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->addVersion(versionNum, host, timestamp);
    }
    return status;
}

int FileSystem::updateNetworkTimestamp(string fileName, int versionNum, Host * host, int timestamp){
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status == errNoFileFound){
        status = addFile(fileName, versionNum);
        status = findFileIndex(fileName, index);
    }
    DBG_PRINTF(("Entering updateNetworkTimestamp... with fileName %s versionNum %i timestamp %i\n", fileName.c_str(), versionNum, timestamp));
    if(host == NULL){ 
        DBG_PRINTF(("HOST IS NULL IN updateNetworkTimstamp"));
    }    
    status = _files[index]->updateTimestamp(versionNum, host, timestamp);
    if(status == errVersionNotFound){
        status = _files[index]->addVersion(versionNum, host, timestamp);
    }
    pthread_mutex_unlock (&files_mutex);
    return status;
}

int FileSystem::receivedFile(string fileName, int versionNum, int timestamp){
    DBG_PRINTF(("Entering receivedFile... with fileName %s versionNum %i timestamp %i\n", fileName.c_str(), versionNum, timestamp));
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        status = _files[index]->receivedLocalCopy(versionNum);
        status = _files[index]->updateVersion(versionNum, timestamp);
        status = _files[index]->setStatus(File::READY, versionNum);
    }
    pthread_mutex_unlock (&files_mutex);
    DBG_PRINTF(("leaving receivedFile... with status %i\n", status));
    return status;
}

int FileSystem::deleteFile(string fileName, int timestamp){
    int status = SAFE;
    pthread_mutex_lock (&files_mutex);
    int index = 0;
    status = findFileIndex(fileName, index);
    if(status != errNoFileFound){
        // mark file as delete
        status = _files[index]->deleteFile(timestamp);

        // delete physical files
        pthread_mutex_unlock (&files_mutex);
        
        // since this is a disk operation, get mutex
        pthread_mutex_lock (&file_writing_mutex);
        string finaloutput = "";
        // deleting the file...
        // delete all the versions
        for(int i = 0; i <= _files[index]->versionNum();i++){
            finaloutput = _localDirectory+"/"+convertInt(i)+"_"+fileName;
            DBG_PRINTF(( "Deleting file: %s\n", finaloutput.c_str() ));
            // remove the file if exist
            ifstream file(finaloutput.c_str());
            if(file){
                if( remove( finaloutput.c_str() ) != 0 ){
                    DBG_PRINTF(( "Error deleting file: %s\n", finaloutput.c_str() ));
                } else {
                    DBG_PRINTF(( "File successfully deleted %s\n", finaloutput.c_str()  ));
                }
            }
        }   
        pthread_mutex_unlock (&file_writing_mutex);        
    }else{
        pthread_mutex_unlock (&files_mutex);
    }
    return status;
}



